package io.renren.socket.client;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.renren.htby.utils.SocketUtils;
import io.renren.modules.iotp.utils.IotpBodyUtils;
import io.renren.modules.iotp.utils.Msg_rellect;

/**
 * 抽象类，继承需要重写业务方法。
 * 
 * @author lfy.xys
 * @date 2018年6月11日
 *
 */
public abstract class NioAbstractClient {

	private static Logger logger = LoggerFactory.getLogger(NioAbstractClient.class);

	public String ip;
	public int port;
	public List<byte[]> sendMsg;// 发送的消息
	public ByteBuffer rBuffer = ByteBuffer.allocate(8354);
	public int count = 0;
	public SocketChannel client = null;
	public int times = 0;// 重连次数

	public InetSocketAddress SERVER = null;
	public Selector selector = null;
	public SocketChannel socketChannel;
	public int i = 0;
	
	 /**
     * 数据拼接工具类
     */
    public Msg_rellect mr = new Msg_rellect();
    public List<byte[]> byte_list = new ArrayList<>();
    /**
	 * 协议解析工具类
	 */
    public IotpBodyUtils ibu = new IotpBodyUtils();
    /**
	 * 返回通讯的结果（返回值）
	 */
    public byte[] iotp_result = null;
    
	public NioAbstractClient(String ip, int port, List<byte[]> sendMsg) {
		this.ip = ip;
		this.port = port;
		this.sendMsg = sendMsg;
		logger.info(ip + ":" + port);

	}

	/**
	 * 进行连接
	 * 
	 * @author lfy.xys
	 * @date 2018年6月11日
	 *
	 */
	public void connect() {
		// 进行连接
		SERVER = new InetSocketAddress(ip, port);
		try {
			socketChannel = SocketChannel.open();
			socketChannel.configureBlocking(false);
			selector = Selector.open();
			socketChannel.register(selector, SelectionKey.OP_CONNECT);
			socketChannel.connect(SERVER);

			// 连接后，循环执行
			boolean flag = true;
			while (flag) {
				try {
					selector.select();
					Set<SelectionKey> selectionKeys = selector.selectedKeys();
					for (SelectionKey key : selectionKeys) {
						flag = handle(key);
					}
					selectionKeys.clear();
				} catch (Exception e) {
					e.printStackTrace();
					break;
				}

			}

		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			close();
		}
	}

	/**
	 * 连接后
	 * 
	 * @author lfy.xys
	 * @date 2018年6月11日
	 *
	 * @param key
	 * @return
	 * @throws IOException
	 * @throws InterruptedException
	 */
	public boolean handle(SelectionKey selectionKey) throws IOException, InterruptedException {
		
		boolean flag = true;
		if (selectionKey.isConnectable()) {
			client = (SocketChannel) selectionKey.channel();
			if (client.isConnectionPending()) {
				try {
					// 成功连接
					client.finishConnect();
					
					logger.info("connect success ! sendMsg : {}", sendMsg.toString());
					// 发送数据
					sendMsg();
					
				} catch (Exception e) {
					// 关闭当前连接 ，并重连
					close();
					reconnect();
				}
			}
			client.register(selector, SelectionKey.OP_READ);
		} else if (selectionKey.isReadable()) {
			boolean failure = true;// 判断服务端是否关闭
			client = (SocketChannel) selectionKey.channel();
			rBuffer.clear();
			try {
				count = client.read(rBuffer);
				logger.info("count:" + count);
				failure = false;
			} catch (Exception e) {
				logger.info("断开连接");
			}

			if (count <= 0 || failure) {
				selectionKey.cancel(); // Some JDK implementations run into an infinite loop without this.
				logger.info("服务端关闭");
				flag = false;
			} else {
				// 重新读取 指定长度的数据。 否则获取的是创建的缓冲区长度，末尾会被补0。
				rBuffer.clear();
				byte[] data = new byte[count];
				rBuffer.get(data, 0, data.length);
				client.register(selector, SelectionKey.OP_READ);

				byte_list = mr.rellect(data);
				logger.info("拿到第{}次数据。", ++i);
				logger.info(Arrays.toString(data));
				
				if (byte_list != null && byte_list.size() > 0) {
					for (byte[] b : byte_list) {
						// false关闭 当前socket连接.  true 继续接收消息
						flag = receiveMsg(b);
					}
				}
			}
		}

		// false则可以结束
		return flag;
	}

	/**
	 * 客户端 发送数据。
	 * 
	 * @author lfy.xys
	 * @date 2018年6月11日
	 *
	 */
	public abstract void sendMsg();

	/**
	 * 客户端 接收数据
	 * 
	 * @author lfy.xys
	 * @return 
	 * @date 2018年6月11日
	 *
	 */
	public abstract boolean receiveMsg(byte[] bytes);

	/**
	 * 服务端连接不上，客户端重连
	 * 
	 * @author lfy.xys
	 * @date 2018年5月30日
	 *
	 */
	public void reconnect() {

		if (times > SocketUtils.NIO_RECONNECT_TIMES) {
			logger.info("服务端连接失败，超过连接次数，不再重连.");
			return;
		}

		logger.info("服务端连接失败，{}ms后进行重连...", SocketUtils.NIO_RECONNECT_INTERVAL_TIME);
		try {
			Thread.sleep(SocketUtils.NIO_RECONNECT_INTERVAL_TIME);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		connect();
	}

	/**
	 * 关闭数据
	 * 
	 * @author lfy.xys
	 * @date 2018年6月11日
	 *
	 */
	public void close() {
		if (client != null) {
			try {
				rBuffer.clear();
				client.close();
				logger.info("关闭 {}:{} 的连接", ip, port);
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
	}

}
